/**
 * Copyright (C), 2021-2023, 四信物联网有限公司
 * FileName: BeiDouEncoder
 * Author:   Chapin
 * Date:     2023/10/12 15:16
 * Description: 北斗协议编码
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.fourfaith.iot.beidou.decodec;

import cn.hutool.core.util.HexUtil;
import com.alibaba.fastjson.JSONObject;
import com.fourfaith.iot.beidou.message.BeiDouProcotolMessage;
import com.fourfaith.iot.beidou.message.Message;
import com.fourfaith.iot.beidou.message.inbound.BDtciMessage;
import com.fourfaith.iot.beidou.message.outbound.CCtcqMessage;
import com.fourfaith.iot.beidou.utils.ObjectOperate;
import com.fourfaith.iot.device.cloud.access.dubbo.pojo.param.BeiDouParam;
import io.netty.buffer.ByteBufUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.StringJoiner;

/**
 * 〈北斗协议编码〉
 * @author Chapin
 * @create 2023/10/12
 * @since 1.0.0
 */
public class BeiDouEncoder {

    /**
     * 将对象编码成字符串
     * @param beiDouProcotolMessage
     * @return
     */
    public static String encodeToStr(BeiDouProcotolMessage beiDouProcotolMessage){
        beiDouProcotolMessage.setCheckCode(calculateChecksum(beiDouProcotolMessage.getCommandCode() + beiDouProcotolMessage.getData()));
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(beiDouProcotolMessage.getHead())
                .append(beiDouProcotolMessage.getCommandCode())
                .append(beiDouProcotolMessage.getData())
                .append(beiDouProcotolMessage.getCheckFlag())
                .append(beiDouProcotolMessage.getCheckCode())
                .append(new String(beiDouProcotolMessage.getTail(), StandardCharsets.US_ASCII));
        return stringBuffer.toString();
    }

    /**
     * 将对象编码成数组
     * @param beiDouProcotolMessage
     * @return
     */
    public static byte[] encodeToByte(BeiDouProcotolMessage beiDouProcotolMessage){
        String encode = encodeToStr(beiDouProcotolMessage);
        return encode.getBytes(StandardCharsets.US_ASCII);
    }


    /**
     * 将对象编码成数组
     * @param msg
     * @return
     */
    public static byte[] encodeToByte(Message msg) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        ObjectOperate<Message> objectOperate = new ObjectOperate<>();
        Class<?> messageClass = Message.getMessageClass(msg.getCommand());
        Field[] fields = messageClass.getDeclaredFields();
        StringJoiner data = new StringJoiner(",",",","");
        for (Field field : fields) {
            Object value = objectOperate.getValue(msg,field.getName());
            data.add(value.toString());
        }
        BeiDouProcotolMessage beiDouProcotolMessage = new BeiDouProcotolMessage();
        beiDouProcotolMessage.setCommandCode(msg.getCommand());
        beiDouProcotolMessage.setData(data.toString());
        return encodeToByte(beiDouProcotolMessage);
    }

    /**
     * 将对象编码成hex字符串
     * @param beiDouProcotolMessage
     * @return
     */
    public static String encodeToHex(BeiDouProcotolMessage beiDouProcotolMessage){
        byte[] bytes = encodeToByte(beiDouProcotolMessage);
        return ByteBufUtil.hexDump(bytes).toUpperCase(Locale.ROOT);
    }

    /**
     * 解码
     * @param hexStr
     * @return
     */
    public static Object decode(String hexStr)  {
        BeiDouProcotolMessage beiDouProcotolMessage = new BeiDouProcotolMessage();
        beiDouProcotolMessage.setCommandCode(hexStr.substring(hexStr.indexOf(BeiDouContants.head) + 1,hexStr.indexOf(BeiDouContants.separator)));
        beiDouProcotolMessage.setData(hexStr.substring(hexStr.indexOf(BeiDouContants.separator),hexStr.indexOf(BeiDouContants.checkFlag)));
        beiDouProcotolMessage.setCheckCode(hexStr.substring(hexStr.indexOf(BeiDouContants.checkFlag) + 1));
        // 通过反射获取消息对象
        Object object = getMsgByReflect(beiDouProcotolMessage);
        return object;
    }


    /**
     * 通过反射获取消息对象
     * @param beiDouProcotolMessage
     */
    public static Object getMsgByReflect(BeiDouProcotolMessage beiDouProcotolMessage) {
        // 通过反射设置对象属性
        Class<?> messageClass = Message.getMessageClass(beiDouProcotolMessage.getCommandCode());
        if(messageClass == null){
            return null;
        }
        ObjectOperate<Object> objectOperate = new ObjectOperate<>();
        String[] valueArr = beiDouProcotolMessage.getData().substring(1).split(",");
        Object object = null;
        try {
            object = messageClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        Field[] fields = messageClass.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if(i > valueArr.length-1){
                break;
            }
            String name = fields[i].getName();

            String typeName = fields[i].getGenericType().getTypeName();
            try {
                objectOperate.setValues(object,name,typeName,valueArr[i]);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return object;
    }


    /**
     * 异或累加校验和
     * 将一个整数转换为十六进制字符串，并且如果结果不足两位数，则在前面补0
     * @param data
     * @return
     */
    public static String calculateChecksum(String data){
        byte[] bytes = data.getBytes(StandardCharsets.US_ASCII);
        byte checkSum = 0;
        for (byte b : bytes) {
            checkSum ^= b;
        }
        return String.format("%02x",checkSum);
    }

    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        String cctcqHex = "2443435443512c343230323337312c312c312c332c413437453131303230303234373437333635373438323235303030333030314133433533343535343443344634333431344334313443343135323444334433313243333532433332324333303243333133423345314137452c302a37650d0a0d0a";
        String decodeStr = "$CCTCQ,4202371,2,1,3,A47E110200387473657582250119002E3C534554414C41524D5458543DCEC4B1BED4A4BEAFC4A3B0E53030342DC4A3B0E53030342C352C312C302C313B3EF07E,0*70";
        String decodeStr1 = "$BDTCI,1861321,1861321,2,125600,3,0,A47E7E014040252094A000320026020026230616125600F1F1404025209448F0F0230616125620190000103923AAAAAAAA38121197036CE5*7F";
//        String decodeStr = "$CCTCQ,4202371,2,1,3,A47E110200267473657582250121001C3C534554414C41524D4D50333D30303030332C352C312C302C313B3E647E,0*0b";
        Object object = BeiDouEncoder.decode(decodeStr);
        BeiDouParam beiDouParam = new BeiDouParam();
        beiDouParam.setVersion(BeiDouContants.BEI_DOU3);
        beiDouParam.setProtocol(BeiDouContants.PROTOCOL_HYDROLOGICAL);
        if(object instanceof CCtcqMessage){
            byte[] bytes = encodeToByte((Message) object);
            System.out.println(HexUtil.encodeHexStr(bytes));
        }else if(object instanceof BDtciMessage){
            beiDouParam.setFunctionCode(((BDtciMessage) object).getCommand());
            beiDouParam.setMessageContext(((BDtciMessage) object).getData());
            beiDouParam.setReceivedId(((BDtciMessage) object).getAcceptorNumber());
            beiDouParam.setSendId(((BDtciMessage) object).getSenderNumber());
        }

    }

}